package com.tom.architect.party4.day34.retrofit;

import android.util.Log;

import com.google.gson.Gson;
import com.tom.architect.party4.day34.retrofit.http.GET;
import com.tom.architect.party4.day34.retrofit.http.POST;
import com.tom.architect.party4.day34.retrofit.http.Query;
import com.tom.architect.party4.day34.simple.RequestBuilder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import okhttp3.ResponseBody;

public class ServiceMethod {
    final Retrofit retrofit;
    final Method method;
    String httpMethod;
    String relativeUrl;
    final ParameterHandler<?>[] parameterHandlers;

    public ServiceMethod(Builder builder) { // Builder设计模式赋值
        this.retrofit = builder.retrofit;
        this.method = builder.method;
        this.httpMethod = builder.httpMethod;
        this.relativeUrl = builder.relativeUrl;
        this.parameterHandlers = builder.parameterHandlers;
    }

    // args 参数数组
    public okhttp3.Call createNewCall(Object[] args) {
        // 添加参数。一个对象，专门用来添加参数。
        RequestBuilder requestBuilder = new RequestBuilder(retrofit.baseUrl, relativeUrl,
                httpMethod, parameterHandlers, args);
        // 适配器模式：Request 转变成 Call
        return retrofit.callFactory.newCall(requestBuilder.build());
    }

    // 解析数据，返回泛型T对应的数据
    public <T> T parseBody(ResponseBody responseBody) {
        // 获取解析类型 T： 获取方法返回值的类型
        Type returnType = method.getGenericReturnType();// 拿到返回值对象的 所有泛型(可能会放置多个泛型 Call<Bean1, Bean2,Bean3>)
        // UserLoginResult
        Class <T> dataClass = (Class <T>) ((ParameterizedType) returnType).getActualTypeArguments()[0]; // 拿第一个泛型Bean1
        // 解析工厂去转换为对象Bean
        T body = new Gson().fromJson(responseBody.charStream(), dataClass);
        return body;
    }


    // 要解析的参数
    public static class Builder {
        final Retrofit retrofit;
        final Method method;
        final Annotation[] methodAnnotations;
        String httpMethod;
        String relativeUrl;
        // 可能有 @QueryMap,所以要用二维数组。
        Annotation[][] parameterAnnotations;
        final ParameterHandler<?>[] parameterHandlers; // 集合

        public Builder(Retrofit retrofit, Method method) {
            this.retrofit = retrofit;
            this.method = method;
            methodAnnotations = method.getAnnotations();
            // 二维数组，参数的注解处理
            parameterAnnotations = method.getParameterAnnotations();
            parameterHandlers = new ParameterHandler[parameterAnnotations.length];
        }

        public ServiceMethod build() {
            // 解析。url=baseUrl+ relativeUrl; method,
            for (Annotation methodAnnotation : methodAnnotations) { // 方法的 注解 @GET,@DELETE,@Http...
                parseAnnotationMethod(methodAnnotation);
            }
            // 解析参数的注解。@Query
            int count = parameterHandlers.length;
            for (int i = 0; i < count; i++) { // 遍历所有参数的注解
                Annotation parameter = parameterAnnotations[i][0];
                // Query 等等
                // parameter= com.darren.day.retrofit.http.Query
                Log.e("TAG", "parameter= " + parameter.annotationType().getName());
                // 涉及到模板模式，和 策略模式。不同parameter，处理不一样
                if (parameter instanceof Query) {
                    // 一个一个的封装成 ParameterHandler，不同的参数注解选择不同的策略。parameter是key。
                    parameterHandlers[i] = new ParameterHandler.Query<>(((Query) parameter).value());
                }
                // ....
            }
            return new ServiceMethod(this);
        }

        // 解析方法的注解
        private void parseAnnotationMethod(Annotation methodAnnotation) {
            // value，请求方法，
            if (methodAnnotation instanceof GET) {
                parseMethodAndPath("GET", ((GET) methodAnnotation).value());
            } else if (methodAnnotation instanceof POST) {
                parseMethodAndPath("POST", ((POST) methodAnnotation).value());
            }
            //...
        }

        // 解析方法。value
        private void parseMethodAndPath(String method, String value) {
            this.httpMethod = method;
            this.relativeUrl = value;
        }
    }


}
